﻿/****************************** Module Header ******************************\
 * Module Name:  DllLoader.cs
 * Module Version:  1.0.0 (2014-4-3)
 * Copyright (c) AnyEB.Com (14507247@qq.com)
 * 
 * This class is used to load dll.
 * 
 * This source is subject to the GPL License.
 * See http://www.gnu.org/licenses/gpl.html.
 * All other rights reserved.
 * 
 * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
 * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace UCL.IO
{
    /// <summary>
    /// Dll加载器
    /// <para>用于插件式开发中的热加载技术</para>
    /// </summary>
    /// <remarks>2014-4-3 1.0 by sutroon</remarks>
    public class DllLoader
    {
        /// <summary>
        /// 获取或设置是否在创建类型实例的时候进行缓存
        /// <para>如果为真,则在每次创建成功类型实例的时候会将实例缓存到类型实例缓存池中,方便下次取用.</para>
        /// </summary>
        public static bool EnableCaching { get; set; }

        /// <summary>
        /// 获取或设置当前类型名称
        /// <para>用于类型名称字符串重用,节省内存开销</para>
        /// </summary>
        public static string TypeName { get; set; }

        /// <summary>
        /// 获取或设置当前Dll文件的路径
        /// <para>用于路径字符串重用,节省内存开销</para>
        /// </summary>
        public static string DllFilePath { get; set; }

        /// <summary>
        /// 获取或设置应用程序启动目录
        /// </summary>
        public static string ApplicationStartupPath { get; set; }

        /// <summary>
        /// 类型实例
        /// <para>用于在全局存储打开的窗体等数据,可以替代MdiForm的MdiChildren</para>
        /// </summary>
        public struct TypeItem
        {
            public string Name;
            public object TypeObject;
            public object Tag;
            public DateTime Timestamp;
            public short State;
        }
        /// <summary>
        /// 类型实例缓存池
        /// </summary>
        public static IList<TypeItem> lstTypeItems = new List<TypeItem>();

        /// <summary>
        /// 判断类型实例缓存池中是否包含指定名称的类型实例
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static bool Contains(string name)
        {
            foreach (TypeItem i in lstTypeItems)
            {
                if (i.Name == name) return true;
            }
            return false;
        }

        /// <summary>
        /// 设置决定路径
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string SetAbsolutePath(string path)
        {
            if (Path.IsPathRooted(path) || string.IsNullOrEmpty(ApplicationStartupPath)) return path;
            return ApplicationStartupPath + "\\" + path;
        }

        /// <summary>
        /// 设置命名空间
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static string SetNamespace(string name, string dllpath)
        {
            return name.Contains('.')? name: (Path.GetFileNameWithoutExtension(dllpath) + "." + name);
        }

        /// <summary>
        /// 从文件中获取指定名称的类型
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static Type GetTypeFromFile(string path, string name)
        {
            path = SetAbsolutePath(path);
            if (!File.Exists(path)) return null;
            Assembly assemly = Assembly.LoadFile(path);
            name = SetNamespace(name, path);

            return assemly.GetType(name, false, true);
        }

        /// <summary>
        /// 从文件中获取指定名称的类型实例,如果失败则返回异常对象
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static object GetTypeInstanceFromFile(string path, string name)
        {
            Type type = GetTypeFromFile(path, name);
            if (type == null) return null;
            try
            {
                return Activator.CreateInstance(type);
            }
            catch (Exception ex)
            {
                return ex;
            }
        }

        public static object InvokeMethod<T>(string path, string name, Action<T> act, bool cachingFirst = true)
        {
            object oType = cachingFirst ? GetTypeInstanceMutex(path, name) : GetTypeInstanceFromFile(path, name);
            if (!IsValidType(oType)) return null;
            try
            {
                act((T)oType);
            }
            catch (Exception ex)
            {
                return ex;
            }
            return null;
        }

        /// <summary>
        /// 打开一个互斥的类型实例
        /// <para>优先获取缓存池的类型实例，如果没有则从文件中打开</para>
        /// </summary>
        /// <param name="dllpath"></param>
        /// <param name="name"></param>
        /// <param name="method"></param>
        /// <returns></returns>
        public static object GetTypeInstanceMutex(string dllpath, string name)
        {
            return Contains(name) ? GetTypeInstanceFromCaching(name) : GetTypeInstanceFromFile(dllpath, name);
        }

        /// <summary>
        /// 从缓存池中获取指定名称的类型实例
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static object GetTypeInstanceFromCaching(string name)
        {
            foreach (TypeItem i in lstTypeItems)
            {
                if (i.Name == name) return i.TypeObject;
            }
            return null;
        }

        /// <summary>
        /// 判断是否是有效的类型
        /// <para>无效类型包括：null | Exception</para>
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static bool IsValidType(object o)
        {
            return !(o == null || o is Exception);
        }

        /// <summary>
        /// 清空缓存池
        /// </summary>
        public static void ClearCaching()
        {
            lstTypeItems.Clear();
        }
    }
}
